package com.smxy.daydayup.util.osa.impl.local;

import com.aventrix.jnanoid.jnanoid.NanoIdUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.smxy.daydayup.util.TenantContext;
import com.smxy.daydayup.util.osa.OSAFetchController;
import com.smxy.daydayup.util.osa.OSAProvider;
import com.smxy.daydayup.util.osa.ObjectStorageInfo;
import com.smxy.daydayup.util.osa.SelfHelpFileFetcher;
import com.smxy.daydayup.util.osa.mapper.ObjectStorageInfoMapper;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.time.LocalDateTime;

/**
 * @Author: yusy
 * @Date: 2022/8/11
 */
public class LocalDiskOSAProvider implements OSAProvider, SelfHelpFileFetcher {
    private static final Logger log = LoggerFactory.getLogger(LocalDiskOSAProvider.class);

    private final ObjectStorageInfoMapper objectStorageInfoMapper;
    private final LocalDiskProperties localDiskProperties;

    public LocalDiskOSAProvider(LocalDiskProperties localDiskProperties, ObjectStorageInfoMapper objectStorageInfoMapper) {
        this.objectStorageInfoMapper = objectStorageInfoMapper;
        this.localDiskProperties = localDiskProperties;
    }

    @Override
    public ObjectStorageInfo upload(MultipartFile image, CustomeInfo customeInfo) {

        final String bucket = StringUtils.hasText(customeInfo.bucket)? customeInfo.bucket: "default_bucket";

        final String halfPath = (bucket + "/" + customeInfo.path + "/").replaceAll("//", "/");

        final String filePath = localDiskProperties.basePath + halfPath;

        final File dir = new File(filePath);

        dir.mkdirs();
        final String fileName = NanoIdUtils.randomNanoId();

        final String fullFileName = filePath + fileName;
        final File file = new File(fullFileName);

        try {
            image.transferTo(file);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        ObjectStorageInfo res = new ObjectStorageInfo();
        res.setUrl(OSAFetchController.FETCH_URI_PREFIX + halfPath + fileName);
        res.setBucket(bucket);
        res.setFullPath(fullFileName);
        res.setMime(image.getContentType());
        res.setOriginName(image.getOriginalFilename());
        res.setSize(image.getSize());
        final TenantContext.Tenant tenant = TenantContext.getTenant();
        res.setCreatedAt(LocalDateTime.now());
        res.setModifiedAt(LocalDateTime.now());
        res.setCreatedId(tenant.getId());
        res.setCreatedName(tenant.getName());
        res.setMarkedForDel(false);
        res.setModifiedId(tenant.getId());
        res.setModifiedName(tenant.getName());
        objectStorageInfoMapper.insert(res);
        return res;
    }

    @Override
    public void fetch(HttpServletResponse response, String path) throws IOException {
        final ObjectStorageInfo objectStorageInfo = objectStorageInfoMapper.selectOne(Wrappers.lambdaQuery(ObjectStorageInfo.class)
                .select(ObjectStorageInfo::getBucket, ObjectStorageInfo::getMime, ObjectStorageInfo::getSize,
                        ObjectStorageInfo::getFullPath)
                .eq(ObjectStorageInfo::getUrl, path)
        );

        if (objectStorageInfo == null) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        final File file = new File(objectStorageInfo.getFullPath());
        if (!file.exists()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        response.setHeader("Cache-Control", "public, max-age=" + localDiskProperties.cacheMaxAge);
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType(objectStorageInfo.getMime());
        response.setContentLengthLong(objectStorageInfo.getSize());
        try (FileInputStream inputStream = new FileInputStream(file)) {
            IOUtils.copy(inputStream, response.getOutputStream());
        } catch (IOException e) {
            log.warn("IOException:{}, {}",path, e.getMessage());
        }
    }

    @Override
    public byte[] fetchRaw(String path) throws IOException {
        final ObjectStorageInfo objectStorageInfo = objectStorageInfoMapper.selectOne(Wrappers.lambdaQuery(ObjectStorageInfo.class)
                .select(ObjectStorageInfo::getBucket, ObjectStorageInfo::getMime, ObjectStorageInfo::getSize,
                        ObjectStorageInfo::getFullPath)
                .eq(ObjectStorageInfo::getUrl, path)
        );

        if (objectStorageInfo == null) {
            throw new FileNotFoundException(path);
        }
        final File file = new File(objectStorageInfo.getFullPath());
        if (!file.exists()) {
            throw new FileNotFoundException(path);
        }

        try (FileInputStream inputStream = new FileInputStream(file); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {

            IOUtils.copy(inputStream, byteArrayOutputStream);

            return byteArrayOutputStream.toByteArray();
        }
    }
}
